package com.ly.judge.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson2.JSON;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ly.api.domain.UserExeResult;
import com.ly.api.domain.dto.JudgeSubmitDTO;
import com.ly.api.domain.vo.UserQuestionResultVO;
import com.ly.common.core.constants.Constants;
import com.ly.common.core.constants.JudgeConstants;
import com.ly.common.core.enums.CodeRunStatus;
import com.ly.judge.domain.SandBoxExecuteResult;
import com.ly.judge.domain.UserSubmit;
import com.ly.judge.mapper.UserSubmitMapper;
import com.ly.judge.service.IJudgeService;
import com.ly.judge.service.ISandboxPoolService;
import com.ly.judge.service.ISandboxService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * 判题服务实现类
 * 实现IJudgeService接口，提供Java代码判题的核心逻辑
 * 主要功能：
 * 1. 调用沙箱服务执行用户代码
 * 2. 比对执行结果与预期输出
 * 3. 检查时间、空间限制是否满足要求
 * 4. 保存判题结果到数据库
 */
@Service
@Slf4j
public class JudgeServiceImpl implements IJudgeService {

    /**
     * 沙箱服务接口
     * 用于执行用户提交的Java代码
     */
    @Autowired
    private ISandboxService sandboxService;

    /**
     * 沙箱池服务接口
     * 用于从沙箱池中获取可用的沙箱执行代码
     */
    @Autowired
    private ISandboxPoolService sandboxPoolService;

    /**
     * 用户提交记录Mapper
     * 用于操作数据库中的用户提交记录表
     */
    @Autowired
    private UserSubmitMapper userSubmitMapper;

    /**
     * 执行Java代码判题
     * @param judgeSubmitDTO 判题提交数据传输对象
     *       包含用户ID、题目ID、用户代码、输入输出用例等信息
     * @return UserQuestionResultVO 判题结果视图对象
     *        包含判题结果、得分、执行信息等
     */
    @Override
    public UserQuestionResultVO doJudgeJavaCode(JudgeSubmitDTO judgeSubmitDTO) {
        // 记录判题开始日志
        log.info("---- 判题逻辑开始 -------");

        // 1. 调用沙箱池服务执行用户代码
        SandBoxExecuteResult sandBoxExecuteResult =
                sandboxPoolService.exeJavaCode(judgeSubmitDTO.getUserId(), judgeSubmitDTO.getUserCode(), judgeSubmitDTO.getInputList());

        // 2. 初始化判题结果对象
        UserQuestionResultVO userQuestionResultVO = new UserQuestionResultVO();

        // 3. 判断代码执行是否成功
        if (sandBoxExecuteResult != null && CodeRunStatus.SUCCEED.equals(sandBoxExecuteResult.getRunStatus())) {
            // 3.1 执行成功，进行结果比对和限制检查
            userQuestionResultVO = doJudge(judgeSubmitDTO, sandBoxExecuteResult, userQuestionResultVO);
        } else {
            // 3.2 执行失败，设置错误结果
            userQuestionResultVO.setPass(Constants.FALSE);
            if (sandBoxExecuteResult != null) {
                // 3.2.1 有错误信息则设置错误信息
                userQuestionResultVO.setExeMessage(sandBoxExecuteResult.getExeMessage());
            } else {
                // 3.2.2 无错误信息则设置未知错误
                userQuestionResultVO.setExeMessage(CodeRunStatus.UNKNOWN_FAILED.getMsg());
            }
            // 3.3 设置错误分数
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
        }

        // 4. 保存判题结果到数据库
        saveUserSubmit(judgeSubmitDTO, userQuestionResultVO);

        // 5. 记录判题结束日志
        log.info("判题逻辑结束，判题结果为： {} ", userQuestionResultVO);

        // 6. 返回判题结果
        return userQuestionResultVO;
    }

    /**
     * 执行判题逻辑
     * @param judgeSubmitDTO 判题提交数据传输对象
     * @param sandBoxExecuteResult 沙箱执行结果
     * @param userQuestionResultVO 判题结果对象
     * @return 填充后的判题结果对象
     */
    private UserQuestionResultVO doJudge(JudgeSubmitDTO judgeSubmitDTO,
                                         SandBoxExecuteResult sandBoxExecuteResult,
                                         UserQuestionResultVO userQuestionResultVO) {
        // 1. 获取执行输出和预期输出
        List<String> exeOutputList = sandBoxExecuteResult.getOutputList();
        List<String> outputList = judgeSubmitDTO.getOutputList();

        // 2. 检查输出数量是否一致
        if (outputList.size() != exeOutputList.size()) {
            // 2.1 数量不一致，设置错误结果
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setPass(Constants.FALSE);
            userQuestionResultVO.setExeMessage(CodeRunStatus.NOT_ALL_PASSED.getMsg());
            return userQuestionResultVO;
        }

        // 3. 初始化执行结果列表
        List<UserExeResult> userExeResultList = new ArrayList<>();

        // 4. 比对每个测试用例的结果
        boolean passed = resultCompare(judgeSubmitDTO, exeOutputList, outputList, userExeResultList);

        // 5. 组装最终判题结果
        return assembleUserQuestionResultVO(judgeSubmitDTO, sandBoxExecuteResult, userQuestionResultVO, userExeResultList, passed);
    }

    /**
     * 组装判题结果对象
     * @param judgeSubmitDTO 判题提交数据传输对象
     * @param sandBoxExecuteResult 沙箱执行结果
     * @param userQuestionResultVO 判题结果对象
     * @param userExeResultList 用户执行结果列表
     * @param passed 是否通过所有测试用例
     * @return 组装完成的判题结果对象
     */
    private UserQuestionResultVO assembleUserQuestionResultVO(JudgeSubmitDTO judgeSubmitDTO,
                                                              SandBoxExecuteResult sandBoxExecuteResult,
                                                              UserQuestionResultVO userQuestionResultVO,
                                                              List<UserExeResult> userExeResultList, boolean passed) {
        // 1. 设置执行结果列表
        userQuestionResultVO.setUserExeResultList(userExeResultList);

        // 2. 检查是否通过所有测试用例
        if (!passed) {
            // 2.1 未通过，设置错误结果
            userQuestionResultVO.setPass(Constants.FALSE);
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setExeMessage(CodeRunStatus.NOT_ALL_PASSED.getMsg());
            return userQuestionResultVO;
        }

        // 3. 检查内存使用是否超限
        if (sandBoxExecuteResult.getUseMemory() > judgeSubmitDTO.getSpaceLimit()) {
            // 3.1 内存超限，设置错误结果
            userQuestionResultVO.setPass(Constants.FALSE);
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setExeMessage(CodeRunStatus.OUT_OF_MEMORY.getMsg());
            return userQuestionResultVO;
        }

        // 4. 检查执行时间是否超限
        if (sandBoxExecuteResult.getUseTime() > judgeSubmitDTO.getTimeLimit()) {
            // 4.1 时间超限，设置错误结果
            userQuestionResultVO.setPass(Constants.FALSE);
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setExeMessage(CodeRunStatus.OUT_OF_TIME.getMsg());
            return userQuestionResultVO;
        }

        // 5. 所有检查通过，设置成功结果
        userQuestionResultVO.setPass(Constants.TRUE);
        // 5.1 根据题目难度计算得分
        int score = judgeSubmitDTO.getDifficulty() * JudgeConstants.DEFAULT_SCORE;
        userQuestionResultVO.setScore(score);

        return userQuestionResultVO;
    }

    /**
     * 比对执行结果与预期输出
     * @param judgeSubmitDTO 判题提交数据传输对象
     * @param exeOutputList 实际执行输出列表
     * @param outputList 预期输出列表
     * @param userExeResultList 用户执行结果列表（输出参数）
     * @return 是否所有测试用例都通过
     */
    private boolean resultCompare(JudgeSubmitDTO judgeSubmitDTO, List<String> exeOutputList,
                                  List<String> outputList, List<UserExeResult> userExeResultList) {
        // 1. 默认设置为通过
        boolean passed = true;

        // 2. 遍历每个测试用例
        for (int index = 0; index < outputList.size(); index++) {
            // 2.1 获取预期输出和实际输出
            String output = outputList.get(index);
            String exeOutPut = exeOutputList.get(index);
            String input = judgeSubmitDTO.getInputList().get(index);

            // 2.2 创建单个测试用例的执行结果对象
            UserExeResult userExeResult = new UserExeResult();
            userExeResult.setInput(input);
            userExeResult.setOutput(output);
            userExeResult.setExeOutput(exeOutPut);
            userExeResultList.add(userExeResult);

            // 2.3 比对输出结果
            if (!output.equals(exeOutPut)) {
                // 2.3.1 输出不匹配，设置为不通过
                passed = false;
                log.info("输入：{}， 期望输出：{}， 实际输出：{} ", input, output, exeOutputList);
            }
        }

        // 3. 返回比对结果
        return passed;
    }

    /**
     * 保存用户提交记录到数据库
     * @param judgeSubmitDTO 判题提交数据传输对象
     * @param userQuestionResultVO 判题结果视图对象
     */
    private void saveUserSubmit(JudgeSubmitDTO judgeSubmitDTO, UserQuestionResultVO userQuestionResultVO) {
        // 1. 创建数据库实体对象
        UserSubmit userSubmit = new UserSubmit();

        // 2. 复制属性值
        BeanUtil.copyProperties(userQuestionResultVO, userSubmit);

        // 3. 设置其他属性
        userSubmit.setUserId(judgeSubmitDTO.getUserId());
        userSubmit.setQuestionId(judgeSubmitDTO.getQuestionId());
        userSubmit.setExamId(judgeSubmitDTO.getExamId());
        userSubmit.setProgramType(judgeSubmitDTO.getProgramType());
        userSubmit.setUserCode(judgeSubmitDTO.getUserCode());
        userSubmit.setCaseJudgeRes(JSON.toJSONString(userQuestionResultVO.getUserExeResultList()));
        userSubmit.setCreateBy(judgeSubmitDTO.getUserId());

        // 4. 删除旧记录（如果存在）
        userSubmitMapper.delete(new LambdaQueryWrapper<UserSubmit>()
                .eq(UserSubmit::getUserId, judgeSubmitDTO.getUserId())
                .eq(UserSubmit::getQuestionId, judgeSubmitDTO.getQuestionId())
                .isNull(judgeSubmitDTO.getExamId() == null, UserSubmit::getExamId)
                .eq(judgeSubmitDTO.getExamId() != null, UserSubmit::getExamId, judgeSubmitDTO.getExamId()));

        // 5. 插入新记录
        userSubmitMapper.insert(userSubmit);
    }
}